import { default as THREE } from './three.min';
import THREE_BAS from './bas.js';

function Slide(width, height, animationPhase) {
    var plane = new THREE.PlaneGeometry(width, height, width * 2, height * 2);

    THREE_BAS.BAS.Utils.separateFaces(plane);

    var geometry = new SlideGeometry(plane);

    geometry.bufferUVs();

    var aAnimation = geometry.createAttribute('aAnimation', 2);
    var aStartPosition = geometry.createAttribute('aStartPosition', 3);
    var aControl0 = geometry.createAttribute('aControl0', 3);
    var aControl1 = geometry.createAttribute('aControl1', 3);
    var aEndPosition = geometry.createAttribute('aEndPosition', 3);

    var i, i2, i3, i4, v;

    var minDuration = 0.8;
    var maxDuration = 1.2;
    var maxDelayX = 0.9;
    var maxDelayY = 0.125;
    var stretch = 0.11;

    this.totalDuration = maxDuration + maxDelayX + maxDelayY + stretch;

    var startPosition = new THREE.Vector3();
    var control0 = new THREE.Vector3();
    var control1 = new THREE.Vector3();
    var endPosition = new THREE.Vector3();

    var tempPoint = new THREE.Vector3();

    function getControlPoint0(centroid) {
        var signY = Math.sign(centroid.y);

        tempPoint.x = THREE.Math.randFloat(0.1, 0.3) * 50;
        tempPoint.y = signY * THREE.Math.randFloat(0.1, 0.3) * 70;
        tempPoint.z = THREE.Math.randFloatSpread(20);

        return tempPoint;
    }

    function getControlPoint1(centroid) {
        var signY = Math.sign(centroid.y);

        tempPoint.x = THREE.Math.randFloat(0.3, 0.6) * 50;
        tempPoint.y = -signY * THREE.Math.randFloat(0.3, 0.6) * 70;
        tempPoint.z = THREE.Math.randFloatSpread(20);

        return tempPoint;
    }

    for (i = 0, i2 = 0, i3 = 0, i4 = 0; i < geometry.faceCount; i++, i2 += 6, i3 += 9, i4 += 12) {
        var face = plane.faces[i];
        var centroid = THREE_BAS.BAS.Utils.computeCentroid(plane, face);

        // animation
        var duration = THREE.Math.randFloat(minDuration, maxDuration);
        var delayX = THREE.Math.mapLinear(centroid.x, -width * 0.5, width * 0.5, 0.0, maxDelayX);
        var delayY;

        if (animationPhase === 'in') {
            delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, 0.0, maxDelayY);
        } else {
            delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, maxDelayY, 0.0);
        }

        for (v = 0; v < 6; v += 2) {
            aAnimation.array[i2 + v] = delayX + delayY + Math.random() * stretch * duration;
            aAnimation.array[i2 + v + 1] = duration;
        }

        // positions

        endPosition.copy(centroid);
        startPosition.copy(centroid);

        if (animationPhase === 'in') {
            control0.copy(centroid).sub(getControlPoint0(centroid));
            control1.copy(centroid).sub(getControlPoint1(centroid));
        } else {
            // out
            control0.copy(centroid).add(getControlPoint0(centroid));
            control1.copy(centroid).add(getControlPoint1(centroid));
        }

        for (v = 0; v < 9; v += 3) {
            aStartPosition.array[i3 + v] = startPosition.x;
            aStartPosition.array[i3 + v + 1] = startPosition.y;
            aStartPosition.array[i3 + v + 2] = startPosition.z;

            aControl0.array[i3 + v] = control0.x;
            aControl0.array[i3 + v + 1] = control0.y;
            aControl0.array[i3 + v + 2] = control0.z;

            aControl1.array[i3 + v] = control1.x;
            aControl1.array[i3 + v + 1] = control1.y;
            aControl1.array[i3 + v + 2] = control1.z;

            aEndPosition.array[i3 + v] = endPosition.x;
            aEndPosition.array[i3 + v + 1] = endPosition.y;
            aEndPosition.array[i3 + v + 2] = endPosition.z;
        }
    }

    var material = new THREE_BAS.BAS.BasicAnimationMaterial(
        {
            shading: THREE.FlatShading,
            side: THREE.DoubleSide,
            uniforms: {
                uTime: { type: 'f', value: 0 },
            },
            shaderFunctions: [
                THREE_BAS.BAS.ShaderChunk['cubic_bezier'],
                //THREE_BAS.BAS.ShaderChunk[(animationPhase === 'in' ? 'ease_out_cubic' : 'ease_in_cubic')],
                THREE_BAS.BAS.ShaderChunk['ease_in_out_cubic'],
                THREE_BAS.BAS.ShaderChunk['quaternion_rotation'],
            ],
            shaderParameters: [
                'uniform float uTime;',
                'attribute vec2 aAnimation;',
                'attribute vec3 aStartPosition;',
                'attribute vec3 aControl0;',
                'attribute vec3 aControl1;',
                'attribute vec3 aEndPosition;',
            ],
            shaderVertexInit: [
                'float tDelay = aAnimation.x;',
                'float tDuration = aAnimation.y;',
                'float tTime = clamp(uTime - tDelay, 0.0, tDuration);',
                'float tProgress = ease(tTime, 0.0, 1.0, tDuration);',
                //'float tProgress = tTime / tDuration;'
            ],
            shaderTransformPosition: [
                animationPhase === 'in' ? 'transformed *= tProgress;' : 'transformed *= 1.0 - tProgress;',
                'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);',
            ],
        },
        {
            map: new THREE.Texture(),
        }
    );

    THREE.Mesh.call(this, geometry, material);

    this.frustumCulled = false;
}
Slide.prototype = Object.create(THREE.Mesh.prototype);
Slide.prototype.constructor = Slide;
Object.defineProperty(Slide.prototype, 'time', {
    get: function () {
        return this.material.uniforms['uTime'].value;
    },
    set: function (v) {
        this.material.uniforms['uTime'].value = v;
    },
});

Slide.prototype.setImage = function (image) {
    // var canvas = createRoundedImage(image, [8, 8, 8, 8]);

    this.material.uniforms.map.value.image = image;
    this.material.uniforms.map.value.needsUpdate = true;

};

Slide.prototype.transition = function () {
    return TweenMax.fromTo(this, 3.0, { time: 0.0 }, { time: this.totalDuration, ease: Power0.easeInOut });
};

function SlideGeometry(model) {
    THREE_BAS.BAS.ModelBufferGeometry.call(this, model);
}
SlideGeometry.prototype = Object.create(THREE_BAS.BAS.ModelBufferGeometry.prototype);
SlideGeometry.prototype.constructor = SlideGeometry;
SlideGeometry.prototype.bufferPositions = function () {
    var positionBuffer = this.createAttribute('position', 3).array;

    for (var i = 0; i < this.faceCount; i++) {
        var face = this.modelGeometry.faces[i];
        var centroid = THREE_BAS.BAS.Utils.computeCentroid(this.modelGeometry, face);

        var a = this.modelGeometry.vertices[face.a];
        var b = this.modelGeometry.vertices[face.b];
        var c = this.modelGeometry.vertices[face.c];

        positionBuffer[face.a * 3] = a.x - centroid.x;
        positionBuffer[face.a * 3 + 1] = a.y - centroid.y;
        positionBuffer[face.a * 3 + 2] = a.z - centroid.z;

        positionBuffer[face.b * 3] = b.x - centroid.x;
        positionBuffer[face.b * 3 + 1] = b.y - centroid.y;
        positionBuffer[face.b * 3 + 2] = b.z - centroid.z;

        positionBuffer[face.c * 3] = c.x - centroid.x;
        positionBuffer[face.c * 3 + 1] = c.y - centroid.y;
        positionBuffer[face.c * 3 + 2] = c.z - centroid.z;
    }
};

function createRoundedImage(image, radius) {
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    canvas.width = image.width;
    canvas.height = image.height;

    ctx.beginPath();
    ctx.moveTo(radius[0], 0);
    ctx.lineTo(canvas.width - radius[1], 0);
    ctx.arcTo(canvas.width, 0, canvas.width, radius[1], radius[1]);
    ctx.lineTo(canvas.width, canvas.height - radius[2]);
    ctx.arcTo(canvas.width, canvas.height, canvas.width - radius[2], canvas.height, radius[2]);
    ctx.lineTo(radius[3], canvas.height);
    ctx.arcTo(0, canvas.height, 0, canvas.height - radius[3], radius[3]);
    ctx.lineTo(0, radius[0]);
    ctx.arcTo(0, 0, radius[0], 0, radius[0]);
    ctx.closePath();

    ctx.clip();

    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

    return canvas;
}
export default Slide;
